﻿Public Class Form1

    ' ╔═════════════════════════ Набор API-функций ═══════════════════════╗
    '  ┌─────────────────────────────────────────────────────────────────┐
    Declare Auto Function ShellAbout Lib "shell32.dll" Alias "ShellAboutW" ( _
    ByVal hWnd As Integer, _
    ByVal szApp As String, _
    ByVal szOtherStuff As String, _
    ByVal hIcon As Integer) _
    As Integer
    '  └─────────────────────────────────────────────────────────────────┘
    '  ┌─────────────────────────────────────────────────────────────────┐
    Declare Auto Function FindWindow Lib "user32.dll" Alias "FindWindowW" ( _
    ByVal lpClassName As String, _
    ByVal lpWindowName As String) _
    As Integer
    ' Функция FindWindow находит родительское окно верхнего
    '       уровня с совпадающими lpClassName и lpWindowName.
    ' Параметры:
    ' lpClassName: Имя класса окна (заканчивающееся пустым символом).
    ' lpWindowName: Текстовый заголовок окна или 0, если все окна.
    ' Возвращаемое значение:
    ' Описатель окна; 0 - если такого окна нет.
    ' Функция находится в файле user32.dll
    '  └─────────────────────────────────────────────────────────────────┘
    '  ┌─────────────────────────────────────────────────────────────────┐
    Declare Auto Function SetWindowPos Lib "user32.dll" Alias "SetWindowPos" ( _
    ByVal hWnd As Integer, _
    ByVal hWndInsertAfter As Integer, _
    ByVal x As Integer, _
    ByVal y As Integer, _
    ByVal cx As Integer, _
    ByVal cy As Integer, _
    ByVal wFlags As Integer) _
    As Integer
    ' Функция SetWindowPos изменяет размер, положение и порядок окна.
    ' Параметры:
    ' hWnd: Идентификатор окна.
    ' hWndInsertAfter: Предыдущее окно в списке администратора окон.
    ' x, y: Верхний левый угол.
    ' cx: Новая ширина окна.
    ' cy: Новая высота окна.
    ' Возвращаемое значение:
    ' Один из флагов swp_DrawFrame, swp_HideWindow, swp_NoActivate,
    ' swp_NoRemove, swp_NoSize, swp_NoRedraw, swp_NoZorder, swp_ShowWindow.
    ' Функция находится в файле user32.dll
    '  └─────────────────────────────────────────────────────────────────┘
    '  ┌─────────────────────────────────────────────────────────────────┐
    Declare Auto Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
    ByVal hWnd As Integer, _
    ByVal wMsg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Byte) _
    As Integer
    ' Функция SendMessage посылает сообщение оконной функции указанного
    '       окна.
    ' Параметры:
    ' hWnd: Окно, принимающее сообщение или $FFFF для посылки 
    '       всем всплывающим окнам в системе.
    ' wMsg: Тип сообщения.
    ' wParam: Дополнительная информация о сообщении.
    ' lParam: Дополнительная информация о сообщении.
    ' Возвращаемое значение:
    ' Значение, возвращенное принимающей оконной функцией.
    ' Функция находится в файле user32.dll
    '  └─────────────────────────────────────────────────────────────────┘
    '  ┌─────────────────────────────────────────────────────────────────┐
    Public Declare Function SetFocus Lib "user32.dll" Alias "SetFocus" ( _
    ByVal hWnd As Integer) _
    As Integer
    ' Функция SetFocus назначает фокус ввода окну, направляя в него весь
    '       ввод с клавиатуры.
    ' Параметры:
    ' hWnd: Идентификатор окна или 0 для игнорирования нажатий клавиш.
    ' Возвращаемое значение:
    ' Предыдущее окно, имевшее фокус ввода; 0 - если такого окна нет.
    ' Функция находится в файле user32.dll
    '  └─────────────────────────────────────────────────────────────────┘
    ' ╚═══════════════════════════════════════════════════════════════════╝
    ' ╔════════════════════════════  Константы  ══════════════════════════╗
    Const _
    Zero As Byte = 0, _
    One As Byte = 1, _
    Two As Byte = 2, _
    Three As Byte = 3, _
    Four As Byte = 4, _
    Five As Byte = 5, _
    Six As Byte = 6, _
    Seven As Byte = 7, _
    Eight As Byte = 8, _
    Nine As Byte = 9

    Const _
    Backspace As Byte = 10, _
    CE As Byte = 20, _
    C As Byte = 30, _
    IsEqual As Byte = 40, _
    Add As Byte = 50, _
    Subtract As Byte = 60, _
    Multiply As Byte = 70, _
    Divide As Byte = 80, _
    Sqrt As Byte = 90, _
    Percent As Byte = 100, _
    OneToX As Byte = 110, _
    MS As Byte = 120, _
    MR As Byte = 130, _
    MPlus As Byte = 140, _
    MC As Byte = 150, _
    Sign As Byte = 160, _
    Dot As Byte = 170, _
    ErrorMessage As Byte = 254, _
    NOP As Byte = 255
    ' ╚═══════════════════════════════════════════════════════════════════╝
    ' ╔════ Переменные для работы с основными функциями калькулятора ═════╗
    Public a As Decimal     ' Переменная для хранения первого числа.
    Public b As Decimal     ' Переменная для второго числа.
    Public e As Byte        ' Equal - флаг нажатия кнопки '='.
    Public f As Byte        ' Fractional - флаг "дробности" числа.
    Public m As Decimal     ' Memory - переменная для хранения чисел,
    '                               сбрасываемых в память.
    Public r As Decimal     ' Result - переменная, хранящая результат
    '                               последнего вычисления.
    Public s As Byte        ' Sign - переменная для хранения
    '                               знака операции.
    Public t As Byte        ' Terminator - флаг завершения ввода
    '                               числа (не цифры, а числа!!!).
    ' ╚═══════════════════════════════════════════════════════════════════╝
    ' ╔════════════════════════ Прочие  переменные ═══════════════════════╗
    Public separator As Char    ' Символ разделителя целой и
    '                             дробной части числа.
    ' ╚═══════════════════════════════════════════════════════════════════╝

    Private Sub act(ByRef num As Byte)

        Text1.Enabled = True            ' Делаем доступным Text1 для того, чтобы он мог получать фокус.
        SetFocus(Me.Text1.Handle)       ' Ставим на Text1 фокус.
        Text1.Enabled = False           ' Делаем Text1 недоступным. Так фокус теряют все объекты.
        SetFocus(Me.Handle)             ' Ставим фокус на форму, чтобы с ним можно было работать через клаву.

        If t = 1 Then r = 0 : t = 0 ' Если ввод предыдущего числа завершен, то
        ' очищаем переменную, хранящую результат последней операции и
        ' обнуляем флаг конца ввода числа.

        Select Case num             ' Здесь начинается проверка значения параметра процедуры.

            Case Zero To Nine       ' Добавление цифры к отображаемому числу.
                If e = 1 Then act(CE) : e = 0 ' Если кнопка "=" была нажата, то
                ' удаляем отображаемое число и обнуляем флаг нажатия кнопки "=".

                If f = 1 Then       ' Если вводится дробное число (т.е. флаг дробности = 1), то
                    Label1.Text = RTrim(Label1.Text) & num & " "    ' Берем текст с Label1, убираем
                    ' лишние пробелы справа, добавляем введенное число и пробел. Затем обработанный
                    ' текст вписываем (пихаем, втюхиваем...) обратно в Label1.
                    Exit Sub
                Else                ' Если вводится целое число, то
                    Try
                        r = Decimal.Add(Decimal.Multiply(r, 10), num)   ' текущее число умножаем на
                        ' десять, прибавляем введенное число и все это присваиваем переменной r.
                    Catch ex As System.OverflowException    ' Если произошло переполнение, то
                        act(NOP)                            ' просто освежаем содержимое Label1.
                    End Try
                End If

                '--------------------------------------------------------
            Case Backspace          ' Удаление последней цифры отображаемого числа.
                r = Int(Decimal.Divide(r, 10))  ' Текущее число делится на десять и результат
                ' становится текущим. При этом отбрасывается дробная часть.
                '--------------------------------------------------------
            Case CE                 ' Удаление отображаемого числа.
                r = 0   ' Обнуляем текущее число и
                f = 0   ' флаг дробности.
                '--------------------------------------------------------
            Case C              ' Очистка калькулятора для новых вычислений.
                a = 0   ' Обнуляем переменную для первого числа,
                b = 0   ' переменную для второго числа,
                r = 0   ' текущее число,
                s = 0   ' знак операции,
                f = 0   ' флаг дробности и
                e = 0   ' флаг нажатия кнопки "=".
                '--------------------------------------------------------
            Case IsEqual        ' Выполнение указанной операции над двумя последними числами.
                If f = 1 Then r = Val(Label1.Text) : f = 0 ' Если флаг дробности установлен, то
                ' текущее число будет равно численному значению текста с Label1.
                If b = 0 Then b = r ' Если b=0, то b=r.
                f = 0               ' Обнуляем флаг дробности.

                Select Case s       ' Проверяем значение переменной s.
                    Case Add        ' если s=Add, то выполняется сложение:
                        Try
                            a += b  ' Эта запись эквивалентна записи   a = a + b.
                        Catch ex As System.OverflowException
                            act(ErrorMessage)
                        End Try
                        '--------------------------------------------------------
                    Case Subtract   ' если s=Subtract, то выполняется вычитание:
                        Try
                            a -= b  ' Эта запись эквивалентна записи   a = a - b.
                        Catch ex As System.OverflowException
                            act(ErrorMessage)
                        End Try
                        '--------------------------------------------------------
                    Case Multiply   ' если s=Multiply, то выполняется умножение:
                        Try
                            a *= b  ' Эта запись эквивалентна записи   a = a * b.
                        Catch ex As System.OverflowException
                            act(ErrorMessage)
                        End Try
                        '--------------------------------------------------------
                    Case Divide     ' если s=Divide, то выполняется деление:
                        Try
                            a /= b  ' Эта запись эквивалентна записи   a = a / b.
                        Catch ex As System.OverflowException
                            act(ErrorMessage)
                        End Try

                End Select

                r = a       ' Результат выполненной операции становится текущим значением Label1.
                e = 1       ' Установка флага нажатия кнопки "=".
                '--------------------------------------------------------
            Case Add, Subtract, Multiply, Divide    ' Вычисление суммы, разности, произведения и частного
                If f = 1 Then r = Val(Label1.Text) : f = 0 ' Если флаг дробности установлен, то
                ' текущее число будет равно численному значению текста с Label1.
                If s <> 0 And e = 0 Then act(IsEqual) ' Если была нажата кнопка какой-либо
                ' арифметической операции, то выполняем соответствующую операцию.
                a = r
                s = num
                t = 1
                b = 0
                f = 0
                '--------------------------------------------------------
            Case Sqrt           ' Вычисление квадратного корня из отображаемого числа.
                r = Math.Sqrt(r)
                '--------------------------------------------------------
            Case Percent        ' Отображение результата умножения в процентах.
                r *= (a / 100)      ' Эквивалентная запись  r = r * (a / 100)
                '--------------------------------------------------------
            Case OneToX         ' Вычисление частного от деления единицы на отображаемое число.
                If r <> 0 Then r = (1 / r) ' Если r≠0, то r = 1/r
                '--------------------------------------------------------
            Case MS             ' Занесение отображаемого числа в память.
                m = r
                If m <> 0 Then Label2.Text = "M"
                '--------------------------------------------------------
            Case MR             ' Замена отображаемого числа числом, хранящимся в памяти.
                r = m
                '--------------------------------------------------------
            Case MPlus          ' Прибавление отображаемого числа к числу, хранящемуся в памяти.
                m += r
                If m = 0 Then
                    Label2.Text = ""
                Else
                    Label2.Text = "M"
                End If
                '--------------------------------------------------------
            Case MC             ' Очистка памяти.
                m = 0
                Label2.Text = ""
                '--------------------------------------------------------
            Case Sign           ' Изменение знака отображаемого числа на противоположный.
                r *= -1     ' Другими словами -    r = r * (-1)
                '--------------------------------------------------------
            Case Dot            ' Вставка символа десятичного разделителя.
                f = 1
                '--------------------------------------------------------
            Case ErrorMessage   ' Обработка ошибок, связанных с переполнением

                MsgBox("Недопустимо большое или малое значение!", _
                       MsgBoxStyle.Critical, "Ошибка")
                act(C)
                '--------------------------------------------------------
            Case NOP            ' "Пустая" операция
                r = r
        End Select


        If Int(r) = r Then
            Label1.Text = r & separator & " "
            If КоличествоToolStripMenuItem.Checked = True Then Label1.Text = Format(r, "#,0.###############") & separator & " "
        Else
            Label1.Text = r & " "
            If КоличествоToolStripMenuItem.Checked = True Then Label1.Text = Format(r, "#,0.###############") & " "
        End If

    End Sub ' главный функциональный блок

    Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
        Select Case e.KeyChar
            Case "0"
                act(Zero)
            Case "1"
                act(One)
            Case "2"
                act(Two)
            Case "3"
                act(Three)
            Case "4"
                act(Four)
            Case "5"
                act(Five)
            Case "6"
                act(Six)
            Case "7"
                act(Seven)
            Case "8"
                act(Eight)
            Case "9"
                act(Nine)
            Case ChrW(Keys.Back)
                act(Backspace)
            Case ChrW(Keys.Escape)
                act(C)
            Case ChrW(Keys.Enter)
                act(IsEqual)
            Case "+"
                act(Add)
            Case "-"
                act(Subtract)
            Case "*"
                act(Multiply)
            Case "/"
                act(Divide)
            Case "@"
                act(Sqrt)
            Case "%"
                act(Percent)
            Case "r"
                act(OneToX)
            Case ChrW(Keys.Delete)
                act(Dot)
        End Select
    End Sub ' обработка ввода с клавиатуры

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        separator = Mid((Format(0.1, "#.#")), 1)
        cmdDot.Text = separator
        act(Zero)
    End Sub ' при загрузке формы...

    Private Sub Form1_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
        MenuStrip1.Enabled = True
    End Sub ' при получении формой фокуса

    Private Sub Form1_Deactivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Deactivate
        MenuStrip1.Enabled = False
    End Sub ' при потере фокуса приложением

    Private Sub КопироватьToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles КопироватьToolStripMenuItem.Click
        Clipboard.SetText(Label1.Text)
    End Sub ' Копирование содержимого бокса в буфер обмена

    Private Sub ПравкаToolStripMenuItem_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs) Handles ПравкаToolStripMenuItem.DropDownOpening
        If Clipboard.ContainsText And IsNumeric(Clipboard.GetText) Then
            ВставитьToolStripMenuItem.Enabled = True
        Else
            ВставитьToolStripMenuItem.Enabled = False
        End If
    End Sub ' Проверка содержимого буфера

    Private Sub ВставитьToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ВставитьToolStripMenuItem.Click
        act(Zero)
        r = Clipboard.GetText
        act(NOP)
    End Sub ' Вставка в бокс из буфера обмена

    Private Sub CEToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CEToolStripMenuItem.Click
        act(CE)
    End Sub ' Удаление отображаемого числа.

    Private Sub MCToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MCToolStripMenuItem.Click
        act(MC)
    End Sub ' Очистка памяти.

    Private Sub MRToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MRToolStripMenuItem.Click
        act(MR)
    End Sub ' Замена отображаемого числа числом, хранящимся в памяти.

    Private Sub MSToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MSToolStripMenuItem.Click
        act(MS)
    End Sub ' Занесение отображаемого числа в память.

    Private Sub MToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MToolStripMenuItem.Click
        act(MPlus)
    End Sub ' Прибавление отображаемого числа к числу, хранящемуся в памяти.

    Private Sub ToolStripMenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem2.Click
        act(Sign)
    End Sub ' Изменение знака отображаемого числа на противоположный.

    Private Sub ИнженерныйToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ИнженерныйToolStripMenuItem.Click

        Call Shell("calc.exe", AppWinStyle.NormalFocus)
        Dim hWnd As Integer
        hWnd = FindWindow("SciCalc", "Калькулятор")
        SetWindowPos(hWnd, 0, Me.Location.X, Me.Location.Y, Me.Width * 1.845, Me.Height, 0)
        SendMessage(hWnd, 273, 304, 0)
        End

    End Sub ' Переход в инженерный режим

    Private Sub КоличествоToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles КоличествоToolStripMenuItem.Click
        act(NOP)
    End Sub ' Форматирование вывода

    Private Sub ВызовСправкиToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ВызовСправкиToolStripMenuItem.Click
        Call Shell("hh.exe " & Environ("WINDIR") & "\Help\calc.chm", _
                   AppWinStyle.NormalFocus)
    End Sub ' Вызов справки

    Private Sub ОПрограммеToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ОПрограммеToolStripMenuItem.Click
        ShellAbout(Me.Handle, Me.Text, "", Me.Icon.Handle)
    End Sub ' О программе

    Private Sub cmd0_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd0.MouseUp
        act(Zero)
    End Sub

    Private Sub cmd1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd1.MouseUp
        act(One)
    End Sub

    Private Sub cmd2_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd2.MouseUp
        act(Two)
    End Sub

    Private Sub cmd3_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd3.MouseUp
        act(Three)
    End Sub

    Private Sub cmd4_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd4.MouseUp
        act(Four)
    End Sub

    Private Sub cmd5_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd5.MouseUp
        act(Five)
    End Sub

    Private Sub cmd6_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd6.MouseUp
        act(Six)
    End Sub

    Private Sub cmd7_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd7.MouseUp
        act(Seven)
    End Sub

    Private Sub cmd8_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd8.MouseUp
        act(Eight)
    End Sub

    Private Sub cmd9_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd9.MouseUp
        act(Nine)
    End Sub

    Private Sub cmdBackspace_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdBackspace.MouseUp
        act(Backspace)
    End Sub ' Удаление последней цифры отображаемого числа.

    Private Sub cmdCE_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdCE.MouseUp
        act(CE)
    End Sub ' Удаление отображаемого числа.

    Private Sub cmdC_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdC.MouseUp
        act(C)
    End Sub ' Очистка калькулятора для новых вычислений.

    Private Sub cmdIsEqual_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdIsEqual.MouseUp
        act(IsEqual)
    End Sub ' Выполнение указанной операции над двумя последними числами.

    Private Sub cmdAdd_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdAdd.MouseUp
        act(Add)
    End Sub ' Вычисление суммы.

    Private Sub cmdSubtract_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdSubtract.MouseUp
        act(Subtract)
    End Sub ' Вычисление разности.

    Private Sub cmdMultiply_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdMultiply.MouseUp
        act(Multiply)
    End Sub ' Вычисление произведения.

    Private Sub cmdDivide_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdDivide.MouseUp
        act(Divide)
    End Sub ' Вычисление частного.

    Private Sub cmdSqrt_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdSqrt.MouseUp
        act(Sqrt)
    End Sub ' Вычисление квадратного корня из отображаемого числа.

    Private Sub cmdPercent_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdPercent.MouseUp
        act(Percent)
    End Sub ' Отображение результата умножения в процентах.

    Private Sub cmd1toX_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmd1toX.MouseUp
        act(OneToX)
    End Sub ' Вычисление частного от деления единицы на отображаемое число.

    Private Sub cmdMS_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdMS.MouseUp
        act(MS)
    End Sub ' Занесение отображаемого числа в память.

    Private Sub cmdMR_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdMR.MouseUp
        act(MR)
    End Sub ' Замена отображаемого числа числом, хранящимся в памяти.

    Private Sub cmdM_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdM.MouseUp
        act(MPlus)
    End Sub ' Прибавление отображаемого числа к числу, хранящемуся в памяти.

    Private Sub cmdMC_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdMC.MouseUp
        act(MC)
    End Sub ' Очистка памяти.

    Private Sub cmdSign_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdSign.MouseUp
        act(Sign)
    End Sub ' Изменение знака отображаемого числа на противоположный.

    Private Sub cmdDot_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmdDot.MouseUp
        act(Dot)
    End Sub ' Вставка символа десятичного разделителя.

End Class